﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using DevExpress.XtraEditors;
using FileManagerClient_Update.UpdateClientService;
using FileManagerGlobal_Library;
using Microsoft.Win32;

namespace FileManagerClient_Update
{
    public partial class ClientUpdate : DevExpress.XtraEditors.XtraForm
    {
        public string ClientVersion { get; set; }
        public ClientUpdate()
        {
            InitializeComponent();
            LookAndFeel.SetSkinStyle("Wizard97");
            StartPosition = FormStartPosition.CenterScreen;
        }

        private void ClientUpdate_FormClosing(object sender, FormClosingEventArgs e)
        {
            XtraMessageBox.Show("更新程序尚未结束，请稍等!");
            e.Cancel = true;
        }
        Client_UpdateServiceClient updateService = new Client_UpdateServiceClient();
        private static UpdateFileInfo serverPath = new UpdateFileInfo();
        private void ClientUpdate_Shown(object sender, EventArgs e)
        {
            if(string.IsNullOrEmpty(ClientVersion))
            {
                XtraMessageBox.Show("更新程序错误，请检查客户端程序版本是否正确!","更新提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
                Environment.Exit(Environment.ExitCode);
            }
            else
            {
                if(updateService.State != CommunicationState.Opened)
                    updateService.Open();
                serverPath = updateService.CheckClientUpdateInfo();
                if(null == serverPath || serverPath.UpdateError == UpdateError.ServerHasProblem)
                {
                    XtraMessageBox.Show("服务器发生异常，请联系管理员!", "更新提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    Environment.Exit(Environment.ExitCode);
                    return;
                }
                if(ClientVersion.Equals(serverPath.ApplicationVersion,StringComparison.CurrentCultureIgnoreCase))
                {
                    XtraMessageBox.Show("客户端已经是最新版本!", "更新提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    Environment.Exit(Environment.ExitCode);
                }
                else
                {
                    var processes = Process.GetProcesses();
                    foreach (var process in processes)
                    {
                        var processName = process.ProcessName.Replace(".exe", "");
                        processName = processName.Replace(".vshost.exe", "");
                        processName = processName.Replace(".vshost", "");
                        if (!GlobalVariable.ClientAppVariable.ClientApplicationName.StartsWith(processName)) continue;
                        process.Kill();
                    }
                    Lb_State.Text = "正在与服务创建连接";
                    var startDownloadingWorker = new BackgroundWorker();
                    startDownloadingWorker.DoWork += startDownloadingWorker_DoWork;
                    startDownloadingWorker.RunWorkerAsync(serverPath);
                }
            }
        }

        private string updateFolder = "UpdateFolder";
        void startDownloadingWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var versionInfo = e.Argument as UpdateFileInfo;
            if(null == versionInfo)return;
            //Check file
            var folder = Path.Combine(Application.StartupPath, updateFolder);
            if (!Directory.Exists(folder))
                Directory.CreateDirectory(folder);
            var filePath = Path.Combine(folder, versionInfo.FileName);
            if (File.Exists(filePath))
                File.Delete(filePath);
            File.Create(filePath).Close();
            File.Open(filePath, FileMode.Open, FileAccess.Write, FileShare.ReadWrite).SetLength(
                versionInfo.FileLength);

            var downloadCollection = new List<DownloadClass>();
            var transferLength = versionInfo.FileLength / GlobalVariable.ClientAppVariable.DownloadNumberOfThreads;
            var progressMax = 0;

            for (var i = 0; i < GlobalVariable.ClientAppVariable.DownloadNumberOfThreads; i++)
            {
                var tempClass = new DownloadClass
                                    {
                                        FileOffset = i*transferLength,
                                        LocalPath = filePath
                                    };
                if (i + 1 == GlobalVariable.ClientAppVariable.DownloadNumberOfThreads)
                    tempClass.TransferLength = (transferLength + versionInfo.FileLength % GlobalVariable.ClientAppVariable.DownloadNumberOfThreads);
                else
                    tempClass.TransferLength = transferLength;
                progressMax += (int)(tempClass.TransferLength / GlobalVariable.ClientAppVariable.DownloadBlockSize);
                if (tempClass.TransferLength % GlobalVariable.ClientAppVariable.DownloadBlockSize > 0)
                    progressMax++;
                tempClass.FileEndOffset = tempClass.FileOffset + tempClass.TransferLength;
                downloadCollection.Add(tempClass);
            }
            Pbc_DownloadUpdate.Invoke((EventHandler) delegate
                                                         {
                                                             Pbc_DownloadUpdate.Properties.Maximum = progressMax;
                                                             Pbc_DownloadUpdate.Position = 0;
                                                             Pbc_DownloadUpdate.Update();
                                                         });
            foreach (var downloadClass in downloadCollection)
            {
                var downloadWorker = new BackgroundWorker();
                downloadWorker.DoWork += downloadWorker_DoWork;
                downloadWorker.RunWorkerAsync(downloadClass);
            }
        }
        static  ReaderWriterLockSlim readerWriterLockSlim = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
        void downloadWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var downloadObj = e.Argument as DownloadClass;
            if (null == downloadObj) return;
            var downloadService = new Client_UpdateServiceClient();
            if(downloadService.State != CommunicationState.Opened)
                downloadService.Open();
            Lb_State.Invoke((EventHandler) delegate
                                               {
                                                   Lb_State.Text = "开始下载";
                                               });
            var fileDownloadArgs = new FileDownloadingArgs
            {
                Offsetk__BackingField = downloadObj.FileOffset
            };
            var fileLength = downloadObj.FileEndOffset - downloadObj.FileOffset;
            while (fileLength != fileDownloadArgs.DownloadSizek__BackingField)
            {
                fileDownloadArgs.DataLengthk__BackingField =
                    fileLength - fileDownloadArgs.DownloadSizek__BackingField <= GlobalVariable.ClientAppVariable.DownloadBlockSize
                        ? fileLength - fileDownloadArgs.DownloadSizek__BackingField
                        : GlobalVariable.ClientAppVariable.DownloadBlockSize;
                var fileData = downloadService.DownloadClientUpdate(fileDownloadArgs);
                readerWriterLockSlim.EnterWriteLock();
                {
                    using(var fileStream = new FileStream(downloadObj.LocalPath, FileMode.Open, FileAccess.Write,
                                    FileShare.ReadWrite))
                    {
                        var writer = new BinaryWriter(fileStream);
                        fileStream.Position = fileDownloadArgs.Offsetk__BackingField;
                        writer.Write(fileData.DataTransferk__BackingField);
                        writer.Close();
                    }
                }
                readerWriterLockSlim.ExitWriteLock();
                var length = fileData.DataTransferk__BackingField.Length;
                fileDownloadArgs.Offsetk__BackingField += length;
                fileDownloadArgs.DownloadSizek__BackingField += length;
                fileData.DataTransferk__BackingField = null;
                downloadObj.FileOffset += length;
                downloadObj.TransferLength -= length;
                SetDownloadingProgress(downloadObj);
            }
        }
        private static object LockFile = new object();
        private void SetDownloadingProgress(DownloadClass downloadObj)
        {
            Invoke((EventHandler) delegate
                                      {
                                          if (Pbc_DownloadUpdate.Position + 1 <= Pbc_DownloadUpdate.Properties.Maximum)
                                              Pbc_DownloadUpdate.Position += 1;
                                          Pbc_DownloadUpdate.Update();
                                          if (Pbc_DownloadUpdate.Position != Pbc_DownloadUpdate.Properties.Maximum)
                                              return;
                                          var partFolder = new DirectoryInfo(Application.StartupPath).Parent;
                                          Lb_State.Text = "下载完成，正在更新程序!";
                                          Pbc_DownloadUpdate.Position = 0;
                                          Pbc_DownloadUpdate.Properties.Maximum = 100;
                                          Pbc_DownloadUpdate.Update();
                                          try
                                          {
                                              Pbc_DownloadUpdate.Position = 50;
                                              Pbc_DownloadUpdate.Update();
                                              ZipHelper.UnZip(downloadObj.LocalPath, partFolder.FullName, "", true);
                                              Lb_State.Text = "正在更新版本信息!";
                                              var appPath = Registry.LocalMachine.OpenSubKey(GlobalVariable.ClientAppRegistryInfo.RegistryUnInstallKey);
                                              if (appPath != null)
                                              {
                                                  var unInstallKey = appPath.OpenSubKey(GlobalVariable.ClientAppRegistryInfo.RegistryUnInstallName);
                                                  if (unInstallKey != null)
                                                  {
                                                      var names = unInstallKey.GetValueNames();
                                                      foreach (var name in names)
                                                      {
                                                          if (
                                                              name.Equals(
                                                                  GlobalVariable.ClientAppRegistryInfo.
                                                                      UninstallNameCollection.DisplayVersion,
                                                                  StringComparison.CurrentCultureIgnoreCase))
                                                              unInstallKey.SetValue(
                                                                  GlobalVariable.ClientAppRegistryInfo.
                                                                      UninstallNameCollection.DisplayVersion,
                                                                  serverPath.ApplicationVersion);
                                                      }
                                                  }
                                                      
                                              }
                                          }
                                          catch (Exception ex)
                                          {
                                              XtraMessageBox.Show("更新异常，异常信息：" + ex.Message, "更新提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                                          }
                                          Pbc_DownloadUpdate.Position = 100;
                                          Pbc_DownloadUpdate.Update();
                                          Lb_State.Text = "完成!";
                                          XtraMessageBox.Show("程序已完成更新，点击确定重启程序!", " 安装提示", MessageBoxButtons.OK,
                                                              MessageBoxIcon.Information);

                                          var clientApplicationPath = Path.Combine(partFolder.FullName,
                                                                                   GlobalVariable.ClientAppVariable.
                                                                                       ClientApplicationName);
                                          if (File.Exists(downloadObj.LocalPath))
                                              File.Delete(downloadObj.LocalPath);
                                          Process.Start(clientApplicationPath);
                                          Environment.Exit(Environment.ExitCode);
                                      });
        }
    }

    public class DownloadClass
    {
        public string LocalPath { get; set; }
        public long TransferLength { get; set; }
        public long FileOffset { get; set; } //开始位置
        public long FileEndOffset { get; set; }//结束位置
    }
}